import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { io, Socket } from 'socket.io-client'
import { Avatar, Button, Input, Upload, message as antdMessage, Tooltip, Spin, Empty, Modal, Select } from 'antd'
import type { UploadProps } from 'antd/es/upload/interface'
import {
  MessageCircle,
  Hash,
  Users,
  Send,
  ImageIcon,
  Mic,
  StopCircle,
  Paperclip,
  RefreshCw,
  Loader2,
  PhoneCall,
} from 'lucide-react'
import axios from 'axios'

import { chatAPI, type ChatMessage, type ChatRoom, type ChatMessageType, type ChatPresenceUser } from '@/utils/api'
import { getUserInfo } from '@/utils/auth'

const SOCKET_URL = import.meta.env.VITE_SOCKET_URL || 'http://localhost:3000'
const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000'

const cx = (...classes: Array<string | false | null | undefined>) => classes.filter(Boolean).join(' ')

interface DraftMessage {
  text: string
}

const formatTime = (time?: string | number | Date) => {
  if (!time) return ''
  const date = new Date(time)
  return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
}

const formatDateHeading = (time?: string | number | Date) => {
  if (!time) return ''
  const date = new Date(time)
  return date.toLocaleDateString('zh-CN', { month: 'long', day: 'numeric', weekday: 'short' })
}

const getMessageTypeByFile = (file: File): ChatMessageType => {
  if (file.type.startsWith('image/')) return 'image'
  if (file.type.startsWith('video/')) return 'video'
  if (file.type.startsWith('audio/')) return 'audio'
  return 'file'
}

const mapRooms = (rooms: ChatRoom[], currentUserId?: string | number) =>
  rooms.map((room) => ({
    ...room,
    unread: room.unread ?? 0,
    label:
      room.type === 'private'
        ? room.name || room.members.find((m) => m !== String(currentUserId)) || '私聊'
        : room.name,
  }))

const ChatCenter: React.FC = () => {
  const userInfo = getUserInfo()
  const userId = userInfo?.id ? String(userInfo.id) : undefined
  const username = userInfo?.displayName || userInfo?.username || '访客'
  const [rooms, setRooms] = useState<Array<ChatRoom & { label?: string }>>([])
  const [allRooms, setAllRooms] = useState<Array<ChatRoom & { label?: string }>>([])
  const [currentRoomId, setCurrentRoomId] = useState<string | null>(null)
  const [messages, setMessages] = useState<ChatMessage[]>([])
  const [loadingRooms, setLoadingRooms] = useState(false)
  const [loadingMessages, setLoadingMessages] = useState(false)
  const [hasMoreMessages, setHasMoreMessages] = useState(true)
  const [drafts, setDrafts] = useState<Record<string, DraftMessage>>({})
  const [inputValue, setInputValue] = useState('')
  const [onlineUsers, setOnlineUsers] = useState<ChatPresenceUser[]>([])
  const [socketInstance, setSocketInstance] = useState<Socket | null>(null)
  const [uploading, setUploading] = useState(false)
  const [isRecording, setIsRecording] = useState(false)
  const [recordingDuration, setRecordingDuration] = useState(0)
  const [privateModalVisible, setPrivateModalVisible] = useState(false)
  const [selectedPrivateUser, setSelectedPrivateUser] = useState<string>('')
  const [creatingPrivate, setCreatingPrivate] = useState(false)
  const [editingRoomId, setEditingRoomId] = useState<string | null>(null)
  const [editingRoomName, setEditingRoomName] = useState<string>('')
  const [updatingRoom, setUpdatingRoom] = useState(false)
  const recordingTimerRef = useRef<NodeJS.Timeout | null>(null)
  const mediaRecorderRef = useRef<MediaRecorder | null>(null)
  const audioChunksRef = useRef<Blob[]>([])
  const messagesEndRef = useRef<HTMLDivElement | null>(null)
  const scrollContainerRef = useRef<HTMLDivElement | null>(null)
  const tempMediaUrlMap = useRef<Record<string, string>>({})
  const currentRoomIdRef = useRef<string | null>(null)

  const scrollToBottom = useCallback(() => {
    requestAnimationFrame(() => {
      messagesEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })
    })
  }, [])

  const addTempMessage = useCallback(
    (type: ChatMessageType, url: string, metadata?: ChatMessage['metadata']) => {
      if (!currentRoomId) return null
      const tempId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`
      tempMediaUrlMap.current[tempId] = url
      const tempMessage: ChatMessage = {
        id: tempId,
        roomId: currentRoomId,
        senderId: userId || '',
        senderName: username,
        type,
        content: url,
        metadata,
        mentions: [],
        readBy: [userId || ''],
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
      }
      setMessages((prev) => [...prev, tempMessage])
      scrollToBottom()
      return tempId
    },
    [currentRoomId, userId, username, scrollToBottom]
  )

  const removeTempMessage = useCallback((tempId: string | null | undefined) => {
    if (!tempId) return
    setMessages((prev) => prev.filter((item) => item.id !== tempId))
    const url = tempMediaUrlMap.current[tempId]
    if (url) {
      URL.revokeObjectURL(url)
      delete tempMediaUrlMap.current[tempId]
    }
  }, [])

  const currentRoom = useMemo(() => rooms.find((room) => room.id === currentRoomId) || null, [rooms, currentRoomId])

  const fetchRooms = useCallback(async () => {
    if (!userId) return
    setLoadingRooms(true)
    try {
      const response = await chatAPI.listRooms(userId)
      if (response.code === 200) {
        const mapped = mapRooms(Array.isArray(response.data) ? response.data : [], userId)
        setRooms(mapped)
        setAllRooms(mapped)
        if (!currentRoomId && mapped.length > 0) {
          setCurrentRoomId(mapped[0].id)
          const draftText = drafts[mapped[0].id]?.text ?? ''
          setInputValue(draftText)
        }
      }
    } catch (error) {
      console.error('[Chat] 获取房间失败', error)
      antdMessage.error('获取聊天室失败，请稍后再试')
    } finally {
      setLoadingRooms(false)
    }
  }, [userId, currentRoomId, drafts])

  const fetchMessages = useCallback(
    async (roomId: string, options?: { before?: string; append?: boolean }) => {
      if (!roomId) return
      setLoadingMessages(true)
      try {
        const response = await chatAPI.fetchMessages({ roomId, limit: 30, before: options?.before })
        if (response.code === 200) {
          const list = Array.isArray(response.data) ? response.data : []
          if (options?.append) {
            setMessages((prev) => [...list, ...prev])
          } else {
            setMessages(list)
            scrollToBottom()
          }
          setHasMoreMessages(list.length >= 30)
        }
      } catch (error) {
        console.error('[Chat] 获取消息失败', error)
        antdMessage.error('加载消息失败')
      } finally {
        setLoadingMessages(false)
      }
    },
    [scrollToBottom]
  )

  useEffect(() => {
    fetchRooms()
  }, [fetchRooms])

  useEffect(() => {
    if (!currentRoomId) return
    currentRoomIdRef.current = currentRoomId
    fetchMessages(currentRoomId)
    const draftText = drafts[currentRoomId]?.text ?? ''
    setInputValue(draftText)
  }, [currentRoomId, fetchMessages, drafts])

  useEffect(() => {
    if (!userId) return
    const socket = io(SOCKET_URL, {
      transports: ['websocket'],
      auth: {
        userId,
        username,
        avatar: '',
      },
    })

    setSocketInstance(socket)

    socket.on('chat:message', (message: ChatMessage) => {
      // 使用ref获取最新的currentRoomId，避免闭包问题
      const currentRoom = currentRoomIdRef.current

      // 更新房间列表
      const updater = (list: Array<ChatRoom & { label?: string }>) =>
        list.map((room) =>
          room.id === message.roomId
            ? {
                ...room,
                lastMessage: {
                  messageId: message.id,
                  preview: message.type === 'text' ? message.content.slice(0, 120) : `[${message.type.toUpperCase()}]`,
                  type: message.type,
                  senderId: message.senderId,
                  senderName: message.senderName,
                  createdAt: message.createdAt,
                },
                unread: room.id === currentRoom ? 0 : (room.unread || 0) + 1,
              }
            : room
        )

      setRooms((prev) => updater(prev))
      setAllRooms((prev) => updater(prev))

      // 只处理当前房间的消息
      if (message.roomId !== currentRoom) {
        return
      }

      setMessages((prev) => {
        // 检查消息是否已存在（通过ID）
        const existingIndex = prev.findIndex((item) => item.id === message.id)
        if (existingIndex !== -1) {
          // 消息已存在，更新它
          const next = [...prev]
          next[existingIndex] = message
          scrollToBottom()
          return next
        }

        // 消息不存在，查找匹配的临时消息
        // 匹配条件：临时消息、相同发送者、相同内容（或相似内容）
        const tempIndex = prev.findIndex(
          (item) =>
            item.id.startsWith('temp-') &&
            item.senderId === message.senderId &&
            item.roomId === message.roomId &&
            (item.content === message.content ||
              (item.type === message.type && Math.abs(new Date(item.createdAt).getTime() - new Date(message.createdAt).getTime()) < 5000))
        )

        if (tempIndex !== -1) {
          // 找到匹配的临时消息，替换它
          const next = [...prev]
          const tempId = next[tempIndex].id
          if (tempMediaUrlMap.current[tempId]) {
            URL.revokeObjectURL(tempMediaUrlMap.current[tempId])
            delete tempMediaUrlMap.current[tempId]
          }
          next[tempIndex] = message
          scrollToBottom()
          return next
        }

        // 没有匹配的临时消息，添加新消息
        // 确保消息按时间顺序插入
        const messageTime = new Date(message.createdAt).getTime()
        const insertIndex = prev.findIndex((item) => {
          const itemTime = new Date(item.createdAt).getTime()
          return itemTime > messageTime
        })

        if (insertIndex === -1) {
          // 新消息是最新的，添加到末尾
          scrollToBottom()
          return [...prev, message]
        } else {
          // 插入到正确的位置
          const next = [...prev]
          next.splice(insertIndex, 0, message)
          scrollToBottom()
          return next
        }
      })
    })

    socket.on('chat:presence', (list: ChatPresenceUser[]) => {
      setOnlineUsers(list)
    })

    socket.on('chat:error', (payload: { message?: string }) => {
      if (payload?.message) {
        antdMessage.error(payload.message)
      }
    })

    return () => {
      socket.off('chat:message')
      socket.off('chat:presence')
      socket.off('chat:error')
      socket.disconnect()
      setSocketInstance(null)
    }
  }, [userId, username, scrollToBottom])

  useEffect(() => {
    if (!socketInstance) return
    const timer = setInterval(() => {
      socketInstance.emit('chat:updatePresence')
    }, 15000)
    return () => {
      clearInterval(timer)
    }
  }, [socketInstance])

  useEffect(() => {
    scrollToBottom()
  }, [messages.length, scrollToBottom])

  useEffect(() => {
    if (!socketInstance || !currentRoomId || !userId || !messages.length) return
    const unreadMessageIds = messages
      .filter((item) => !(item.readBy || []).includes(userId))
      .map((item) => item.id)
    if (!unreadMessageIds.length) return

    socketInstance.emit('chat:read', { roomId: currentRoomId, messageIds: unreadMessageIds })
    chatAPI.markRead({ roomId: currentRoomId, userId, messageIds: unreadMessageIds }).catch((error) => {
      console.error('[Chat] 更新已读状态失败', error)
    })
  }, [socketInstance, currentRoomId, userId, messages])

  const handleSelectRoom = (roomId: string) => {
    if (!roomId || roomId === currentRoomId) return
    setDrafts((prev) => ({
      ...prev,
      [currentRoomId || '']: { text: inputValue },
    }))
    setCurrentRoomId(roomId)
    currentRoomIdRef.current = roomId
    setMessages([])
    setRooms((prev) => prev.map((room) => (room.id === roomId ? { ...room, unread: 0 } : room)))
    setAllRooms((prev) => prev.map((room) => (room.id === roomId ? { ...room, unread: 0 } : room)))
  }

  const handleStartEditRoom = (room: ChatRoom & { label?: string }, e: React.MouseEvent) => {
    e.stopPropagation()
    setEditingRoomId(room.id)
    setEditingRoomName(room.name || '')
  }

  const handleSaveRoomName = async (roomId: string) => {
    const trimmedName = editingRoomName.trim()
    if (!trimmedName) {
      antdMessage.warning('房间名称不能为空')
      return
    }
    if (trimmedName.length > 50) {
      antdMessage.warning('房间名称不能超过50个字符')
      return
    }
    setUpdatingRoom(true)
    try {
      const response = await chatAPI.updateRoom(roomId, { name: editingRoomName.trim() })
      if (response.code === 200 && response.data) {
        const updatedRoom = response.data
        // 对于私聊房间，label优先显示name，如果没有name则显示对方名字
        const updatedLabel =
          updatedRoom.type === 'private'
            ? updatedRoom.name || updatedRoom.members.find((m: string) => m !== userId) || '私聊'
            : updatedRoom.name
        setRooms((prev) =>
          prev.map((room) =>
            room.id === roomId
              ? { ...room, name: updatedRoom.name, label: updatedLabel }
              : room
          )
        )
        setAllRooms((prev) =>
          prev.map((room) =>
            room.id === roomId
              ? { ...room, name: updatedRoom.name, label: updatedLabel }
              : room
          )
        )
        antdMessage.success('房间名称已更新')
        setEditingRoomId(null)
        setEditingRoomName('')
      } else {
        throw new Error('更新失败')
      }
    } catch (error) {
      console.error('[Chat] 更新房间名称失败', error)
      antdMessage.error('更新房间名称失败，请稍后再试')
    } finally {
      setUpdatingRoom(false)
    }
  }

  const handleCancelEditRoom = () => {
    setEditingRoomId(null)
    setEditingRoomName('')
  }

  const joinedRoomsRef = useRef<Set<string>>(new Set())

  useEffect(() => {
    if (!socketInstance) return

    const ensureJoin = () => {
      if (currentRoomId && !joinedRoomsRef.current.has(currentRoomId)) {
        socketInstance.emit('chat:joinRoom', { roomId: currentRoomId })
        joinedRoomsRef.current.add(currentRoomId)
      }
    }

    ensureJoin()

    const handleConnect = () => {
      joinedRoomsRef.current.clear()
      ensureJoin()
    }

    socketInstance.on('connect', handleConnect)

    return () => {
      socketInstance.off('connect', handleConnect)
    }
  }, [socketInstance, currentRoomId])

  const sendMessage = async (type: ChatMessageType, content: string, metadata?: ChatMessage['metadata']) => {
    if (!socketInstance || !currentRoomId || !content.trim()) return
    socketInstance.emit('chat:sendMessage', {
      roomId: currentRoomId,
      type,
      content,
      metadata,
    })
  }

  const handleSendText = async () => {
    const text = inputValue.trim()
    if (!text) return
    setInputValue('')

    const tempId = `temp-${Date.now()}`
    const tempMessage: ChatMessage = {
      id: tempId,
      roomId: currentRoomId!,
      senderId: userId || '',
      senderName: username,
      type: 'text',
      content: text,
      metadata: {},
      mentions: [],
      readBy: [userId || ''],
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    }

    setMessages((prev) => [...prev, tempMessage])
    scrollToBottom()

    await sendMessage('text', text)
  }

  const beforeUpload: UploadProps['beforeUpload'] = (file) => {
    const isLt100M = file.size / 1024 / 1024 < 100
    if (!isLt100M) {
      antdMessage.error('文件大小不能超过 100MB')
      return Upload.LIST_IGNORE
    }
    return true
  }

  const handleUpload: UploadProps['customRequest'] = async (options) => {
    const { file, onError, onSuccess } = options
    if (!file || !currentRoomId) {
      onError?.(new Error('无效的文件或房间 ID'))
      return
    }
    const realFile = file as File
    const messageType = getMessageTypeByFile(realFile)
    const previewUrl = URL.createObjectURL(realFile)
    const tempId = addTempMessage(messageType, previewUrl, {
      fileName: realFile.name,
      size: realFile.size,
      mimeType: realFile.type,
    })

    try {
      setUploading(true)
      const formData = new FormData()
      formData.append('files', realFile)
      formData.append('userId', userId || 'guest')
      formData.append('userName', username)

      const response = await axios.post(`${API_BASE}/api/mqz/upload`, formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
      })

      if (!response.data?.success || !Array.isArray(response.data?.data)) {
        throw new Error('文件上传失败')
      }

      const [fileInfo] = response.data.data
      const absoluteUrl = fileInfo?.url?.startsWith('http')
        ? fileInfo.url
        : `${API_BASE}${fileInfo.url}`

      await sendMessage(messageType, absoluteUrl, {
        fileName: fileInfo.filename,
        size: fileInfo.size,
        mimeType: fileInfo.mimetype,
      })

      onSuccess?.(response.data, new XMLHttpRequest())
      antdMessage.success('发送成功')
    } catch (error) {
      console.error('[Chat] 媒体消息发送失败', error)
      antdMessage.error('发送失败，请稍后重试')
      removeTempMessage(tempId)
      onError?.(error as Error)
    } finally {
      setUploading(false)
    }
  }

  const startRecording = async () => {
    if (!navigator.mediaDevices?.getUserMedia) {
      antdMessage.warning('当前浏览器不支持语音录制')
      return
    }
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
      const recorder = new MediaRecorder(stream)
      audioChunksRef.current = []

      recorder.ondataavailable = (event) => {
        if (event.data.size > 0) {
          audioChunksRef.current.push(event.data)
        }
      }

      recorder.onstop = async () => {
        const blob = new Blob(audioChunksRef.current, { type: 'audio/webm' })
        const file = new File([blob], `voice-${Date.now()}.webm`, { type: 'audio/webm' })
        const previewUrl = URL.createObjectURL(file)
        const tempId = addTempMessage('audio', previewUrl, {
          fileName: file.name,
          size: file.size,
          mimeType: file.type,
          duration: recordingDuration,
        })

        const formData = new FormData()
        formData.append('files', file)
        formData.append('userId', userId || 'guest')
        formData.append('userName', username)

        try {
          setUploading(true)
          const response = await axios.post(`${API_BASE}/api/mqz/upload`, formData, {
            headers: { 'Content-Type': 'multipart/form-data' },
          })

          if (!response.data?.success || !Array.isArray(response.data?.data)) {
            throw new Error('语音上传失败')
          }

          const [fileInfo] = response.data.data
          const absoluteUrl = `${API_BASE}${fileInfo.url}`

          await sendMessage('audio', absoluteUrl, {
            fileName: fileInfo.filename,
            size: fileInfo.size,
            mimeType: fileInfo.mimetype,
            duration: recordingDuration,
          })
          antdMessage.success('语音消息已发送')
        } catch (error) {
          console.error('[Chat] 语音消息发送失败', error)
          antdMessage.error('语音消息发送失败')
          removeTempMessage(tempId)
        } finally {
          setUploading(false)
          stream.getTracks().forEach((track) => track.stop())
        }
      }

      recorder.start()
      mediaRecorderRef.current = recorder
      setIsRecording(true)
      setRecordingDuration(0)
      recordingTimerRef.current = setInterval(() => {
        setRecordingDuration((prev) => prev + 1)
      }, 1000)
    } catch (error) {
      console.error('[Chat] 启动录音失败', error)
      antdMessage.error('无法开始录音，请检查麦克风权限')
    }
  }

  const stopRecording = () => {
    mediaRecorderRef.current?.stop()
    setIsRecording(false)
    if (recordingTimerRef.current) {
      clearInterval(recordingTimerRef.current)
    }
    setRecordingDuration(0)
  }

  const renderMessageContent = (message: ChatMessage) => {
    switch (message.type) {
      case 'image':
        return (
          <img
            src={message.content}
            alt="图片消息"
            className="max-w-[260px] rounded-xl cursor-pointer transition-transform hover:scale-[1.02]"
            onClick={() => window.open(message.content, '_blank')}
          />
        )
      case 'video':
        return (
          <video src={message.content} controls className="max-w-[320px] rounded-xl shadow" preload="metadata" />
        )
      case 'audio':
        return (
          <div className="flex items-center gap-3">
            <audio controls src={message.content} className="w-48" preload="metadata" />
            {message.metadata?.duration ? (
              <span className="text-xs text-gray-400">{Math.round(message.metadata.duration)}s</span>
            ) : null}
          </div>
        )
      case 'file':
        return (
          <a
            href={message.content}
            target="_blank"
            rel="noreferrer"
            className="flex items-center gap-2 px-3 py-2 rounded-lg bg-slate-100 text-slate-700 hover:bg-slate-200"
          >
            <Paperclip size={16} />
            <span>{message.metadata?.fileName || '文件下载'}</span>
          </a>
        )
      default:
        return <span className="leading-relaxed whitespace-pre-wrap break-words">{message.content}</span>
    }
  }

  const groupedMessages = useMemo(() => {
    const groups: Array<{ heading: string; items: ChatMessage[] }> = []
    let currentHeading: string | null = null
    let currentItems: ChatMessage[] = []

    messages.forEach((message) => {
      const heading = formatDateHeading(message.createdAt)
      if (heading !== currentHeading) {
        if (currentItems.length) {
          groups.push({ heading: currentHeading || '', items: currentItems })
        }
        currentHeading = heading
        currentItems = [message]
      } else {
        currentItems.push(message)
      }
    })

    if (currentItems.length) {
      groups.push({ heading: currentHeading || '', items: currentItems })
    }

    return groups
  }, [messages])

  const handleClearHistory = useCallback(async () => {
    if (!currentRoomId) return
    Modal.confirm({
      title: '确认清空聊天记录？',
      content: '该操作不可恢复，将删除服务器上该会话的全部历史消息。',
      okText: '确认删除',
      okButtonProps: { danger: true },
      cancelText: '取消',
      onOk: async () => {
        try {
          await chatAPI.clearMessages(currentRoomId)
          setMessages([])
          antdMessage.success('聊天记录已清空')
        } catch (error) {
          console.error('[Chat] 清空消息失败', error)
          antdMessage.error('清空失败，请稍后再试')
        }
      },
    })
  }, [currentRoomId])

  return (
    <div className="h-full min-h-[calc(100vh-120px)] flex flex-col gap-4 p-4 bg-gradient-to-br from-slate-50 via-white to-slate-100">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-2xl font-semibold text-slate-800 flex items-center gap-2">
            <MessageCircle size={24} className="text-blue-500" />
            即时通讯中心
          </h1>
          <p className="text-sm text-slate-500 mt-1">支持群聊、私聊、图片、视频、语音等多媒体消息</p>
        </div>
        <Button type="text" icon={<RefreshCw size={16} />} onClick={fetchRooms}>
          刷新列表
        </Button>
      </div>

      <div className="flex-1 grid gap-4" style={{ gridTemplateColumns: '280px minmax(0, 1fr) 230px' }}>
        <aside className="rounded-2xl bg-white border border-slate-200 shadow-sm overflow-hidden flex flex-col">
          <div className="px-4 py-3 border-b border-slate-100 flex items-center gap-3 bg-gradient-to-r from-slate-50 to-white">
            <Hash size={18} className="text-blue-500" />
            <span className="font-medium text-slate-700">会话列表</span>
          </div>
          <div className="p-3">
            <div className="flex items-center justify-between gap-2 px-3">
              <Input.Search
                placeholder="搜索会话"
                allowClear
                className="rounded-xl"
                onSearch={(value) => {
                  if (!value) {
                    setRooms(allRooms)
                    return
                  }
                  const filtered = allRooms.filter((room) =>
                    (room.label || room.name).toLowerCase().includes(value.toLowerCase())
                  )
                  setRooms(filtered)
                }}
              />
              <Tooltip title="发起与在线成员的私聊">
                <Button type="primary" onClick={() => setPrivateModalVisible(true)} disabled={onlineUsers.filter((u) => u.userId !== userId).length === 0}>
                  发起私聊
                </Button>
              </Tooltip>
            </div>
          </div>
          <div className="flex-1 overflow-y-auto px-2 pb-4 space-y-2">
            {loadingRooms ? (
              <div className="flex justify-center py-10">
                <Spin />
              </div>
            ) : rooms.length === 0 ? (
              <div className="py-12"><Empty description="暂无会话" /></div>
            ) : (
              rooms.map((room) => {
                const isActive = room.id === currentRoomId
                const isEditing = editingRoomId === room.id
                return (
                  <div
                    key={room.id}
                    className={cx(
                      'w-full rounded-xl px-4 py-3 transition-all border flex flex-col gap-1 relative',
                      isActive
                        ? 'border-transparent bg-gradient-to-r from-blue-500 to-indigo-500 text-white shadow-lg shadow-blue-500/20'
                        : 'border-transparent bg-white hover:bg-slate-50 text-slate-700 border-slate-100',
                      !isEditing && 'cursor-pointer'
                    )}
                    onClick={() => !isEditing && handleSelectRoom(room.id)}
                  >
                    <div className="flex items-center justify-between relative z-10">
                      {isEditing ? (
                        <div className="flex items-center gap-2 flex-1 mr-2">
                          <Input
                            value={editingRoomName}
                            onChange={(e) => setEditingRoomName(e.target.value)}
                            onPressEnter={() => handleSaveRoomName(room.id)}
                            onBlur={() => {
                              // 延迟取消，避免点击按钮时触发
                              setTimeout(() => {
                                if (editingRoomId === room.id) {
                                  handleCancelEditRoom()
                                }
                              }, 200)
                            }}
                            autoFocus
                            disabled={updatingRoom}
                            className="flex-1"
                            size="small"
                            onClick={(e) => e.stopPropagation()}
                            onKeyDown={(e) => {
                              if (e.key === 'Escape') {
                                handleCancelEditRoom()
                              }
                            }}
                          />
                          <Button
                            type="primary"
                            size="small"
                            loading={updatingRoom}
                            onClick={(e) => {
                              e.stopPropagation()
                              handleSaveRoomName(room.id)
                            }}
                          >
                            保存
                          </Button>
                          <Button
                            size="small"
                            onClick={(e) => {
                              e.stopPropagation()
                              handleCancelEditRoom()
                            }}
                            disabled={updatingRoom}
                          >
                            取消
                          </Button>
                        </div>
                      ) : (
                        <>
                          <span
                            className="font-medium truncate flex-1 mr-2 hover:underline cursor-pointer"
                            onDoubleClick={(e) => {
                              e.stopPropagation()
                              handleStartEditRoom(room, e)
                            }}
                            onClick={(e) => {
                              e.stopPropagation()
                            }}
                            title="双击编辑房间名称"
                          >
                            {room.label || room.name}
                          </span>
                          {room.unread ? (
                            <span className="px-2 h-5 min-w-[20px] rounded-full bg-rose-500 text-white text-xs flex items-center justify-center shadow">
                              {room.unread > 99 ? '99+' : room.unread}
                            </span>
                          ) : null}
                        </>
                      )}
                    </div>
                    {!isEditing && (
                      <>
                        {room.lastMessage ? (
                          <p className={cx('text-sm truncate', isActive ? 'text-blue-50/80' : 'text-slate-400')}>
                            {room.lastMessage?.senderName ? `${room.lastMessage.senderName}: ` : ''}
                            {room.lastMessage?.type === 'text'
                              ? room.lastMessage.preview
                              : `[${(room.lastMessage?.type || '').toUpperCase()}]`}
                          </p>
                        ) : (
                          <p className={cx('text-sm', isActive ? 'text-blue-50/80' : 'text-slate-400')}>暂无消息</p>
                        )}
                      </>
                    )}
                  </div>
                )
              })
            )}
          </div>
        </aside>

        <section className="rounded-2xl bg-white border border-slate-200 shadow-sm flex flex-col">
          <div className="px-6 py-4 border-b border-slate-100 flex items-center justify-between bg-gradient-to-r from-blue-50 via-white to-purple-50">
            <div>
              <h2 className="text-lg font-semibold text-slate-800 flex items-center gap-2">
                <Users size={18} className="text-blue-500" />
                {currentRoom?.label || currentRoom?.name || '请选择一个会话'}
              </h2>
              <p className="text-sm text-slate-500">
                {currentRoom?.type === 'private' ? '私聊频道' : '群聊频道，可发送图片、视频、语音等消息'}
              </p>
            </div>
            <div className="flex items-center gap-4 text-sm text-slate-400">
              <span>{messages.length ? `共 ${messages.length} 条消息` : '暂无消息'}</span>
              <Tooltip title="清空历史记录，仅删除当前会话的消息">
                <Button danger type="link" onClick={() => handleClearHistory()}>
                  清空历史
                </Button>
              </Tooltip>
            </div>
          </div>
          <div
            className="flex-1 overflow-y-auto px-6 py-4 space-y-6 bg-gradient-to-br from-white via-slate-50 to-white"
            ref={scrollContainerRef}
          >
            {hasMoreMessages && currentRoomId && (
              <div className="flex justify-center">
                <Button
                  size="small"
                  loading={loadingMessages}
                  onClick={() => {
                    const first = messages[0]
                    if (first) {
                      fetchMessages(currentRoomId, { before: first.createdAt, append: true })
                    }
                  }}
                >
                  加载更多
                </Button>
              </div>
            )}
            {loadingMessages && messages.length === 0 ? (
              <div className="h-[300px] flex items-center justify-center">
                <Spin indicator={<Loader2 className="animate-spin text-blue-500" />} />
              </div>
            ) : groupedMessages.length === 0 ? (
              <div className="h-[320px] flex flex-col items-center justify-center text-slate-400">
                <MessageCircle className="mb-3" size={32} />
                暂无聊天记录，开始第一条交流吧
              </div>
            ) : (
              groupedMessages.map((group) => (
                <div key={group.heading} className="space-y-4">
                  <div className="flex justify-center">
                    <span className="text-xs px-3 py-1 rounded-full bg-white border border-slate-200 text-slate-400 shadow-sm">
                      {group.heading}
                    </span>
                  </div>
                  {group.items.map((message) => {
                    const isSelf = message.senderId === userId
                    return (
                      <div key={message.id} className={cx('flex gap-3', isSelf ? 'justify-end' : 'justify-start')}>
                        {!isSelf && (
                          <Avatar size={36} className="shadow-sm">
                            {message.senderName?.[0]?.toUpperCase() || 'U'}
                          </Avatar>
                        )}
                        <div className="max-w-[70%] flex flex-col gap-1">
                          <div className={cx('px-4 py-3 rounded-3xl shadow-sm', isSelf ? 'bg-gradient-to-r from-blue-500 to-indigo-500 text-white' : 'bg-white border border-slate-100 text-slate-700')}>
                            {!isSelf && (
                              <div className="text-xs font-medium text-slate-400 mb-1">
                                {message.senderName || '匿名用户'}
                              </div>
                            )}
                            {renderMessageContent(message)}
                          </div>
                          <span className={cx('text-xs', isSelf ? 'text-blue-300 self-end' : 'text-slate-400')}>
                            {formatTime(message.createdAt)}
                          </span>
                        </div>
                        {isSelf && (
                          <Avatar size={36} className="bg-blue-500 text-white shadow-sm">
                            {username?.[0]?.toUpperCase() || '我'}
                          </Avatar>
                        )}
                      </div>
                    )
                  })}
                </div>
              ))
            )}
            <div ref={messagesEndRef} />
          </div>
          <div className="border-t border-slate-100 p-4 bg-white">
            <div className="flex items-center gap-3 mb-3">
              <Upload
                showUploadList={false}
                accept="image/*,video/*"
                beforeUpload={beforeUpload}
                customRequest={handleUpload}
              >
                <Tooltip title="发送图片/视频">
                  <Button shape="circle" icon={<ImageIcon size={18} />} disabled={uploading} />
                </Tooltip>
              </Upload>
              <Upload showUploadList={false} customRequest={handleUpload}>
                <Tooltip title="发送文件">
                  <Button shape="circle" icon={<Paperclip size={18} />} disabled={uploading} />
                </Tooltip>
              </Upload>
              {isRecording ? (
                <Tooltip title="停止录音">
                  <Button
                    shape="circle"
                    icon={<StopCircle size={18} className="text-red-500" />}
                    onClick={stopRecording}
                  />
                </Tooltip>
              ) : (
                <Tooltip title="发送语音">
                  <Button shape="circle" icon={<Mic size={18} />} onClick={startRecording} disabled={uploading} />
                </Tooltip>
              )}
              {isRecording && (
                <span className="text-sm text-rose-500 animate-pulse">
                  正在录音 {recordingDuration}s...
                </span>
              )}
            </div>
            <Input.TextArea
              value={inputValue}
              onChange={(e) => setInputValue(e.target.value)}
              autoSize={{ minRows: 2, maxRows: 5 }}
              placeholder={currentRoom ? '输入消息，Enter 发送，Shift+Enter 换行' : '请选择一个会话'}
              onPressEnter={(e) => {
                if (!e.shiftKey) {
                  e.preventDefault()
                  handleSendText()
                }
              }}
              disabled={!currentRoom || uploading}
              className="rounded-2xl"
            />
            <div className="flex justify-between items-center mt-3">
              <span className="text-xs text-slate-400">支持发送表情、链接以及多媒体文件。</span>
              <Button type="primary" icon={<Send size={16} />} onClick={handleSendText} disabled={!inputValue.trim()}>
                发送
              </Button>
            </div>
          </div>
        </section>

        <aside className="rounded-2xl bg-white border border-slate-200 shadow-sm overflow-hidden flex flex-col">
          <div className="px-4 py-3 border-b border-slate-100 flex items-center gap-2 bg-gradient-to-r from-purple-50 to-white">
            <PhoneCall size={18} className="text-purple-500" />
            <span className="font-medium text-slate-700">在线成员</span>
          </div>
          <div className="flex-1 overflow-y-auto p-4 space-y-3">
            {onlineUsers.length === 0 ? (
              <p className="text-sm text-slate-400">暂无在线成员</p>
            ) : (
              onlineUsers.map((user) => (
                <div key={user.userId} className="flex items-center gap-3 p-3 rounded-xl bg-slate-50 border border-slate-100">
                  <Avatar className="bg-purple-500/90 text-white">
                    {user.username?.[0]?.toUpperCase() || 'U'}
                  </Avatar>
                  <div>
                    <div className="text-sm font-medium text-slate-700">{user.username || `用户 ${user.userId}`}</div>
                    <div className="text-xs text-slate-400">
                      {user.lastActive
                        ? `活跃于 ${formatTime(user.lastActive)}`
                        : '实时在线'}
                    </div>
                  </div>
                </div>
              ))
            )}
          </div>
        </aside>
      </div>

      <Modal
        title="发起私聊"
        open={privateModalVisible}
        onCancel={() => {
          if (!creatingPrivate) {
            setPrivateModalVisible(false)
            setSelectedPrivateUser('')
          }
        }}
        okText="开始聊天"
        cancelText="取消"
        confirmLoading={creatingPrivate}
        onOk={async () => {
          if (!userId || !selectedPrivateUser) {
            antdMessage.warning('请选择一个私聊成员')
            return
          }
          setCreatingPrivate(true)
          try {
            const response = await chatAPI.ensurePrivateRoom({ userId, targetId: selectedPrivateUser })
            if (response.code === 200 && response.data) {
              const roomId = response.data.id
              await fetchRooms()
              setCurrentRoomId(roomId)
              antdMessage.success('私聊会话已创建')
              setPrivateModalVisible(false)
              setSelectedPrivateUser('')
            } else {
              throw new Error(response.message || '创建私聊失败')
            }
          } catch (error) {
            console.error('[Chat] 创建私聊失败', error)
            antdMessage.error('创建私聊失败，请稍后再试')
          } finally {
            setCreatingPrivate(false)
          }
        }}
      >
        <Select
          showSearch
          placeholder="选择一个在线成员"
          value={selectedPrivateUser || undefined}
          onChange={(value) => setSelectedPrivateUser(value)}
          className="w-full"
          filterOption={(input, option) => (option?.label as string).toLowerCase().includes(input.toLowerCase())}
          options={onlineUsers
            .filter((user) => user.userId !== userId)
            .map((user) => ({
              value: user.userId,
              label: user.username || `用户 ${user.userId}`,
            }))}
          notFoundContent={onlineUsers.filter((user) => user.userId !== userId).length === 0 ? '暂无可私聊的在线成员' : null}
        />
      </Modal>
    </div>
  )
}

export default ChatCenter
